package com.randb.easyhttp.request;

import com.randb.easyhttp.body.ProgressResponseCallback;
import com.randb.easyhttp.model.HttpParams;
import com.randb.easyhttp.utils.RequestBodyUtils;
import com.randb.easyhttp.utils.Utils;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.reactivex.Observable;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

/**
 * User: RandBII
 * Date: 2020/5/28
 * Description:
 */
public class BaseBodyRequest<R extends BaseBodyRequest> extends BaseRequest<R> {

    protected String str;
    protected MediaType mediaType;
    protected String json;
    protected Object object;
    protected RequestBody requestBody;
    protected byte[] bs;
    private UploadType currentUploadType = UploadType.PART;

    public enum UploadType {
        PART,
        BODY
    }


    public BaseBodyRequest(String url) {
        super(url);
    }

    public R requestBody(RequestBody body) {
        this.requestBody = body;
        return (R) this;
    }

    public R upString(String s) {
        this.str = s;
        this.mediaType = MediaType.parse("text/plain");
        return (R) this;
    }

    public R upString(String str, MediaType mediaType) {
        this.str = str;
        Utils.checkNotNull(mediaType, "mediaType == null");
        this.mediaType = mediaType;
        return (R) this;
    }

    public R upObject(Object ob) {
        this.object = ob;
        return (R) this;
    }

    public R upJson(String json) {
        this.json = json;
        return (R) this;
    }

    public R upBytes(byte[] bytes) {
        this.bs = bytes;
        return (R) this;
    }

    public R params(String key, File file, ProgressResponseCallback callback) {
        params.put(key, file, callback);
        return (R) this;
    }

    public R params(String key, InputStream is, String fileName, ProgressResponseCallback callback) {
        params.put(key, is, fileName, callback);
        return (R) this;
    }

    public R params(String key, Byte[] bytes, String fileName, ProgressResponseCallback callback) {
        params.put(key, bytes, fileName, callback);
        return (R) this;
    }


    public R addFileParams(String key, List<File> fiels, ProgressResponseCallback callback) {
        params.putFileParams(key, fiels, callback);
        return (R) this;
    }

    public R addFileWrapperParams(String key, List<HttpParams.FileWrapper> fileWrappers) {
        params.putFileWrapperParams(key, fileWrappers);
        return (R) this;
    }


    public R params(String key, File file, String fileName, ProgressResponseCallback callback) {
        params.put(key, file, fileName, callback);
        return (R) this;
    }

    public <T> R params(String key, T file, String fileName,
                        MediaType contentType, ProgressResponseCallback callback) {
        params.put(key, file, fileName, contentType, callback);
        return (R) this;
    }

    public <T> R upLoadType(UploadType type) {
        currentUploadType = type;
        return (R) this;
    }


    @Override
    protected Observable<ResponseBody> generateRequest() {


        if (this.requestBody != null) {
            return apiManager.postBody(url, requestBody);
        } else if (this.json != null) {
            RequestBody jsonBody = RequestBody.Companion.create(this.json,
                    MediaType.parse("application/json:charset=utf-8"));
            return apiManager.postJson(url, jsonBody);
        } else if (this.object != null) {
            return apiManager.postBody(url, object);
        } else if (this.str != null) {
            RequestBody strBody = RequestBody.create(str, mediaType);
            return apiManager.postBody(url, strBody);
        } else if (this.bs != null) {
            RequestBody bsBody = RequestBody.Companion.create(this.bs,
                    MediaType.parse("application/octet-stream"));
            return apiManager.postBody(url, bsBody);
        }
        if (params.fileParamsMap.isEmpty()) {
            return apiManager.post(url, params.urlParamsMap);
        } else {
            if (currentUploadType == UploadType.PART) {
                return uploadFilesWithParts();
            } else {
                return uploadFilesWithBodys();
            }
        }

    }

    private Observable<ResponseBody> uploadFilesWithParts() {

        List<MultipartBody.Part> parts = new ArrayList<>();

        for (Map.Entry<String, String> entry : params.urlParamsMap.entrySet()) {
            parts.add(MultipartBody.Part.createFormData(entry.getKey(), entry.getValue()));
        }

        for (Map.Entry<String, List<HttpParams.FileWrapper>> entry : params.fileParamsMap.entrySet()) {
            List<HttpParams.FileWrapper> fileValues = entry.getValue();
            for (HttpParams.FileWrapper wrapper : fileValues) {
                MultipartBody.Part part = addFile(entry.getKey(), wrapper);
                parts.add(part);
            }
        }
        return apiManager.uploadFiles(url, parts);
    }

    private MultipartBody.Part addFile(String key, HttpParams.FileWrapper wrapper) {
        RequestBody requestBody = getRequestBody(wrapper);
        Utils.checkNotNull(requestBody, "requestBody == null , FileWrapper.File " +
                "must be InputStream,bytes[],File");
        if (wrapper.callback != null) {
            // Todo 待续
            return null;
        } else {
            return MultipartBody.Part.createFormData(key, wrapper.fileName, requestBody);
        }


    }

    private RequestBody getRequestBody(HttpParams.FileWrapper wrapper) {
        RequestBody body = null;
        if (wrapper.file instanceof File) {
            body = RequestBody.Companion.create((File) wrapper.file, wrapper.contentType);
        } else if (wrapper.file instanceof InputStream) {
            body = RequestBodyUtils.create(wrapper.contentType, (InputStream) wrapper.file);
        } else if (wrapper.file instanceof byte[]) {
            body = RequestBody.create((byte[]) wrapper.file, wrapper.contentType);
        }
        return body;
    }

    private Observable<ResponseBody> uploadFilesWithBodys() {

        Map<String, RequestBody> bodyMap = new HashMap<>();

        for (Map.Entry<String, String> entry : params.urlParamsMap.entrySet()) {
            RequestBody body = RequestBody.create(entry.getValue(), MediaType.parse("text/plain"));
            bodyMap.put(entry.getKey(), body);
        }
        for (Map.Entry<String, List<HttpParams.FileWrapper>> entry : params.fileParamsMap.entrySet()) {
            List<HttpParams.FileWrapper> fileValues = entry.getValue();
            for (HttpParams.FileWrapper wrapper : fileValues) {
                RequestBody requestBody = getRequestBody(wrapper);
                // TODO  待续
                bodyMap.put(entry.getKey(), requestBody);
            }

        }

        return apiManager.uploadFiles(url, bodyMap);
    }
}
